﻿/*************************************************************************************

- CLR版本：        4.0.30319.42000
- 类 名 称：       MyZMotionEth
- 机器名称：       YTE
- 命名空间：       LD.BC.ZMotion
- 文 件 名：       MyZMotionEth
- 创建时间：       2022/6/11 15:03:40
- 作    者：       YTE
- 说   明：
- 修改时间：
- 修 改 人：
  *************************************************************************************/

using LD.Host;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;


namespace LD.BC
{
    [Bc(Name = "正运动总线", Brand = BrandType.正运动, Param = "192.168.0.11")]
    /// <summary>
    /// 正运动总线
    /// </summary>
    internal class MyZMotionEth : zmcaux, IBoardCard
    {
        private string[] sportModes = { "定长运动", "定速运动", "回零运动", "往复运动" };
        private string[] stopModes = { "减速停止", "立即停止" };
        private string[] locationModes = { "相对坐标", "绝对坐标" };
        private string[] directionModes = { "负方向", "正方向" };
        private string[] zeroModes = { "一次回零", "一次回零+反找", "两次回零", "一次回零+EZ", "EZ回零", "一次回零+反找"
        ,"原点锁存","原点+EZ锁存","EZ锁存","原点+反向EZ锁","限位一次回零","限位回零反找","限位两次回零" };
        private string[] speedModes = { "低速回零", "高速回零" };
        private string[] pluseModes = { "脉冲/方向", "脉冲/脉冲" };

        public Dictionary<string, string[]> GetParam()
        {
            Dictionary<string, string[]> param = new Dictionary<string, string[]>
            {
                { "sportModes", sportModes },
                { "stopModes", stopModes },
                { "locationModes", locationModes },
                { "directionModes", directionModes },
                { "zeroModes", zeroModes },
                { "speedModes", speedModes },
                { "pluseModes", pluseModes }
            };
            return param;
        }

        private IntPtr _bcId;
        private int _ret;
        private CancellationTokenSource tokenSource = new CancellationTokenSource();
        private ManualResetEvent resetEvent = new ManualResetEvent(true);
        private Task _task = null;
        #region 连接
        public void Connection()
        {
            //板卡连接
            IntPtr id;
            _ret = ZAux_OpenEth("192.168.0.11", out id);
            CmdCheck(_ret);
            _bcId = id;
            if (_bcId != (IntPtr)0)
                Console.WriteLine("已连接");
        }
        /// <summary>
        /// 关闭指定的板卡，释放资源
        /// </summary>
        /// <param name="boardCardId"></param>
        public void Closed()
        {
            tokenSource.Cancel();
            resetEvent.Reset();
            _task?.Wait(2000);
            _ret = ZAux_Close(_bcId);
            CmdCheck(_ret);
        }
        #endregion

        #region 参数初始化
        /// <summary>
        /// 载入config文档
        /// </summary>
        /// <param name="fileName"></param>
        public void LoadConfig(string fileName)
        {
        }
        /// <summary>
        /// 加载机种信息
        /// </summary>
        /// <param name="partNo"></param>
        public void DownAxisParam()
        {
            //下载参数
            foreach (var item in BcFactory.Instance.Config.AxisParam)
            {
                SetAxisParam((ushort)item.AxisId, item);
            }

            //打开使能
            SetSevonAll(true);

        }
        #endregion

        #region 轴信息
        /// <summary>
        /// Get轴位置
        /// </summary>
        /// <param name="axis"></param>
        /// <returns></returns>
        public double GetAxisPosition(ushort axis)
        {
            float v = 0;
            _ret = ZAux_Direct_GetDpos(_bcId, axis, ref v);
            CmdCheck(_ret);
            return v;
        }
        /// <summary>
        /// Set轴位置
        /// </summary>
        /// <param name="axis"></param>
        public void SetAxisPosition(ushort axis, int position)
        {
            _ret = ZAux_Direct_SetDpos(_bcId, axis, position);
            CmdCheck(_ret);
        }
        /// <summary>
        /// 轴运动信号状态
        /// </summary>
        /// <param name="cardNo"></param>
        /// <param name="axis"></param>
        /// <returns></returns>
        public string GetAxisStatus(ushort cardNo, ushort axis)
        {
            int v = 0;
            _ret = ZAux_Direct_GetAxisStatus(_bcId, axis, ref v);
            CmdCheck(_ret);

            return null;
        }
        /// <summary>
        /// 设置正负限位
        /// </summary>
        /// <param name="cardNo">板卡</param>
        /// <param name="axis">轴</param>
        /// <param name="n_limit">负限位</param>
        /// <param name="p_limit">正限位</param>
        public void SetSoftLimit(ushort cardNo, ushort axis, int n_limit, int p_limit)
        {

        }
        /// <summary>
        /// 设置脉冲当量
        /// </summary>
        /// <param name="cardNo">板卡</param>
        /// <param name="axis">轴</param>
        /// <param name="equiv">脉冲量</param>
        public void SetEquiv(dynamic cardNo, ushort axis, double equiv)
        {
            _ret = ZAux_Direct_SetUnits(_bcId, axis, (float)equiv);
            CmdCheck(_ret);
        }
        /// <summary>
        /// 获取脉冲当量
        /// </summary>
        /// <param name="cardNo"></param>
        /// <param name="axis"></param>
        /// <returns></returns>
        public double GetEquiv(ushort cardNo, ushort axis)
        {
            float v = 0;
            _ret = ZAux_Direct_GetUnits(_bcId, axis, ref v);
            CmdCheck(_ret);
            return v;
        }
        #endregion

        #region 轴动作

        /// <summary>
        /// 停止单轴
        /// </summary>
        /// <param name="axis"></param>
        public void Stop(ushort axis)
        {
            /*
             *   0 （缺省）取消当前运动
                 1 取消缓冲的运动
                 2 取消当前运动和缓冲运动
                 3 立即中断脉冲发送
             * */
            _ret = ZAux_Direct_Single_Cancel(_bcId, axis, 3);
            CmdCheck(_ret);
            _ret = ZAux_Direct_Single_Cancel(_bcId, axis, 2);
            CmdCheck(_ret);
        }
        /// <summary>
        /// 急停
        /// </summary>
        /// <param name="axis"></param>
        public void StopALL()
        {
            foreach (var item in BcFactory.Instance.Config.AxisParam)
            {
                Stop((ushort)item.AxisId);
            }
        }
        /// <summary>
        /// 轴移动，
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="direction">方向：0/反方向，1/正方向</param>
        public void Move(ushort axis, ushort direction)
        {
            if (BcFactory.Instance.RunModel == StateEnum.手动模式)
                _ret = ZAux_Direct_SetSpeed(_bcId, axis, (float)BcFactory.Instance.Config.AxisParam[axis].Manual_val);
            else
                _ret = ZAux_Direct_SetSpeed(_bcId, axis, (float)BcFactory.Instance.Config.AxisParam[axis].Max_val);

            int dir = direction;
            if (dir == 0)
                dir = -1;
            //运动方向 1正向 -1负向
            _ret = ZAux_Direct_Single_Vmove(_bcId, axis, dir);
            CmdCheck(_ret);
        }
        /// <summary>
        /// 定量移动（相对）
        /// </summary>
        /// <param name="axis">轴</param>
        /// <param name="direction">方向：0/反方向，1/正方向</param>
        /// <param name="ration_v">定量值</param>
        public void Move_Ration(ushort axis, ushort direction, double ration_v)
        {
            //int sig = 0;
            //if (direction == 0)
            //{
            //    sig = GetIn(axis * 32 + 128);
            //    if (sig == (int)LevelType.高电平)
            //    {
            //        CmdCheck(16);
            //        return;
            //    }
            //}
            //else
            //{
            //    sig = GetIn(axis * 32 + 129);
            //    if (sig == (int)LevelType.高电平)
            //    {
            //        CmdCheck(32);
            //        return;
            //    }
            //}


            if (BcFactory.Instance.RunModel == StateEnum.手动模式)
                _ret = ZAux_Direct_SetSpeed(_bcId, axis, (float)BcFactory.Instance.Config.AxisParam[axis].Manual_val);
            else
                _ret = ZAux_Direct_SetSpeed(_bcId, axis, (float)BcFactory.Instance.Config.AxisParam[axis].Max_val);
            CmdCheck(_ret);

            float to = (float)ration_v;
            int dir = direction;
            if (dir == 0)
                to = -to;
            _ret = ZAux_Direct_Single_Move(_bcId, axis, to);
            CmdCheck(_ret);
        }
        public void Move_Ration(ushort axis, double ration_v)
        {
            if (BcFactory.Instance.RunModel == StateEnum.手动模式)
                _ret = ZAux_Direct_SetSpeed(_bcId, axis, (float)BcFactory.Instance.Config.AxisParam[axis].Manual_val);
            else
                _ret = ZAux_Direct_SetSpeed(_bcId, axis, (float)BcFactory.Instance.Config.AxisParam[axis].Max_val);

            _ret = ZAux_Direct_Single_Move(_bcId, axis, (float)ration_v);
            CmdCheck(_ret);
        }
        /// <summary>
        /// 坐标移动
        /// </summary>
        /// <param name="axis">轴</param>
        /// <param name="location">坐标</param>
        public void Move_Location(ushort axis, double location)
        {
            if (BcFactory.Instance.RunModel == StateEnum.手动模式)
                _ret = ZAux_Direct_SetSpeed(_bcId, axis, (float)BcFactory.Instance.Config.AxisParam[axis].Manual_val);
            else
                _ret = ZAux_Direct_SetSpeed(_bcId, axis, (float)BcFactory.Instance.Config.AxisParam[axis].Max_val);

            _ret = ZAux_Direct_Single_MoveAbs(_bcId, axis, (float)location);
            CmdCheck(_ret);
        }
        /// <summary>
        /// 回原点
        /// </summary>
        /// <param name="axis"></param>
        public void Move_BackZero(ushort axis)
        {
            //Z 相模式mode = 1 时正向回零 ,mode = 2 时负向回零 
            //原点+反找模式mode=3 时正向回零，mode = 4 时负向回零
            //原点+反找+Z 信号模式mode=5 正向回零，mode=6 负向回零
            //原点一次回零模式，mode=8 正向回零，mode=9 负向回零

            _ret = ZAux_Direct_SetSpeed(_bcId, axis, (float)BcFactory.Instance.Config.AxisParam[axis].Zero_val);
            CmdCheck(_ret);

            //原点+反找
            uint dir = 3;
            if (BcFactory.Instance.Config.AxisParam[axis].Zero_dir == 0)
                dir = 4;

            _ret = ZAux_Direct_SetCreep(_bcId, axis, 10);
            CmdCheck(_ret);

            //开始回零
            _ret = ZAux_Direct_Single_Datum(_bcId, axis, (int)dir);

            CmdCheck(_ret);

            //总线驱动器回零待测试
            //_ret = ZAux_BusCmd_Datum(_bcId, axis, dir);
            //CmdCheck(_ret);

            WaitAxisComplete(axis);

            uint state = 0;
            _ret = ZAux_BusCmd_GetHomeStatus(_bcId, axis, ref state);
            CmdCheck(_ret);
            //if (state != 1)
            //    FrmAlalm.Instance.Add(Alalm.回零失败);
        }
        /// <summary>
        /// 回零成功否
        /// </summary>
        /// <param name="axis"></param>
        /// <returns></returns>
        public bool GetHomeStatus(ushort axis)
        {
            uint v = 0;
            _ret = ZAux_BusCmd_GetHomeStatus(_bcId, axis, ref v);
            CmdCheck(_ret);

            if (v == 0)
                return false;
            else
                return true;
        }
        /// <summary>
        /// 等轴运行结束
        /// </summary>
        /// <param name="cardNo"></param>
        /// <param name="axis"></param>
        public void WaitAxisComplete(ushort axis)
        {
            int state = 0;
            //0-运动， 1-停止
            while (state == 0)
            {
                _ret = ZAux_Direct_GetIfIdle(_bcId, axis, ref state);
                CmdCheck(_ret);
                Task.Delay(100).Wait();
            };
        }
        /// <summary>
        /// 等轴运行结束
        /// </summary>
        public void WaitAxisComplete()
        {
            foreach (var item in BcFactory.Instance.Config.AxisParam)
            {
                WaitAxisComplete((ushort)item.AxisId);
            }
        }
        /// <summary>
        /// 是否运动中
        /// </summary>
        /// <returns></returns>
        public bool IsMoving()
        {
            bool result = false;
            int state = 0;
            //0-运动， 1-停止
            foreach (var item in BcFactory.Instance.Config.AxisParam)
            {
                _ret = ZAux_Direct_GetIfIdle(_bcId, item.AxisId, ref state);
                CmdCheck(_ret);
                if (state == 0)
                {
                    result = true;
                    return result;
                }
            }
            return result;
        }
        /// <summary>
        /// 设置轴参数
        /// </summary>
        /// <param name="plus"></param>
        public void SetAxisParam(ushort axis, TB_AxisParam item)
        {
            //脉冲当量
            SetEquiv(_bcId, axis, (float)item.Current_val);
            //脉冲输出模式？
            _ret = ZAux_Direct_SetInvertStep(_bcId, axis, 65);
            CmdCheck(_ret);
            //轴最大速度
            _ret = ZAux_Direct_SetSpeed(_bcId, axis, (float)item.Max_val);
            CmdCheck(_ret);
            //轴加速度
            _ret = ZAux_Direct_SetAccel(_bcId, axis, (float)item.Tacc);
            CmdCheck(_ret);
            //轴减速度
            _ret = ZAux_Direct_SetDecel(_bcId, axis, (float)item.Tdec);
            CmdCheck(_ret);
            //S曲线
            _ret = ZAux_Direct_SetSramp(_bcId, axis, (float)item.S_Para);
            CmdCheck(_ret);
            //轴起始速度
            _ret = ZAux_Direct_SetLspeed(_bcId, axis, (float)item.Min_val);
            CmdCheck(_ret);
        }
        /// <summary>
        /// 设置使能
        /// </summary>
        /// <param name="isEnabled"></param>
        public void SetSevon(ushort axisId, bool isEnabled)
        {
            int enabled = isEnabled ? 1 : 0;
            _ret = ZAux_Direct_SetAxisEnable(_bcId, axisId, enabled);
            CmdCheck(_ret);
        }
        /// <summary>
        /// 所有轴开启使能
        /// </summary>
        /// <param name="axisId"></param>
        /// <param name="isEnabled"></param>
        public void SetSevonAll(bool isEnabled)
        {
            foreach (var item in BcFactory.Instance.Config.AxisParam)
            {
                SetSevon(item.AxisId, isEnabled);
            }
        }
        /// <summary>
        /// 清除轴报警
        /// </summary>
        /// <param name="axisId"></param>
        public void ClearAlart(ushort axisId)
        {
            //mode 模式 0 - 清除当前告警 1 - 清除历史告警 2 - 清除外部输入告警
            _ret = ZAux_BusCmd_DriveClear(_bcId, axisId, 0);
            CmdCheck(_ret);
        }
        #endregion

        #region IO
        /// <summary>
        /// 设置out
        /// </summary>
        /// <param name="outPos"></param>
        /// <param name="low_high"></param>
        public void SetOut(ushort outPos, ushort low_high)
        {
            _ret = ZAux_Direct_SetOp(_bcId, outPos, low_high);
            CmdCheck(_ret);
        }
        public int GetOut(int outPos)
        {
            uint v = 0;
            _ret = ZAux_Direct_GetOp(_bcId, outPos, ref v);
            CmdCheck(_ret);
            return (int)v;
        }

        #endregion

        #region 监控
        public void Start()
        {
            CancellationToken token = tokenSource.Token;
            _task = Task.Run(async () =>
             {

                 while (true)
                 {
                     resetEvent.WaitOne();
                     if (token.IsCancellationRequested)
                         return;

                     //方法
                     Monitor();

                     await Task.Delay(500);
                     //Console.WriteLine("正运动点位监控中........");
                 }
             }, token);
        }

        #endregion

        #region 私有方法
        private void Monitor()
        {
            MonitorModel m_Data = new MonitorModel();
            try
            {
                int io_count = 16;
                int size = io_count / 8;
                #region IO
                byte[] in_b = new byte[size];
                byte[] out_b = new byte[size];
                _ret = ZAux_GetModbusIn(_bcId, 0, io_count, in_b);
                CmdCheck(_ret);
                _ret = ZAux_GetModbusOut(_bcId, 0, io_count, out_b);
                CmdCheck(_ret);

                foreach (var item in in_b)
                {
                    var bin = Convert.ToString(item, 2).PadLeft(8, '0').Reversal().ToList();

                    foreach (var b in bin)
                    {
                        m_Data.DI.Add(Convert.ToInt32(b.ToString()));
                    }
                }
                foreach (var item in out_b)
                {
                    var bin = Convert.ToString(item, 2).PadLeft(8, '0').Reversal().ToList();
                    foreach (var b in bin)
                    {
                        m_Data.DO.Add(Convert.ToInt32(b.ToString()));
                    }
                }
                #endregion
                foreach (var item in BcFactory.Instance.Config.AxisParam)
                {
                    ushort id = item.AxisId;

                    #region 轴状态
                    m_Data.AxisState.Add(id, GetAxisState(id, item.Name));

                    #endregion
                }
            }
            catch (Exception ex)
            {
                //LogHelper.Log($"【板卡监控】{ex.Message}", LogLevel.严重);
                Console.WriteLine(ex.Message);
            }
        }
        /// <summary>
        /// 报警信息
        /// </summary>
        /// <param name="ret"></param>
        private void CmdCheck(int ret)
        {
            if (ret > 0)
            {
                //通知全局
                BcFactory.Instance.RunState(RunEnum.急停);

                if (DicAxisState.ContainsKey(ret))
                    BCAlertHelper.Log($"【{ret}】{DicAxisState[ret]}", LogLevel.严重);
                else
                    BCAlertHelper.Log($"【{ret}】错误代码", LogLevel.严重);

            }
        }

        /// <summary>
        /// 轴状态
        /// </summary>
        /// <param name="axisId"></param>
        /// <returns></returns>
        private AxisStatus GetAxisState(ushort axisId, string axisName)
        {
            AxisStatus axisStatus = new AxisStatus();

            //获取使能信号
            int enable = 0;
            _ret = ZAux_Direct_GetAxisEnable(_bcId, axisId, ref enable);
            CmdCheck(_ret);
            axisStatus.使能 = enable;

            //检测回零信号
            //uint sig = 0;
            //_ret = ZAux_BusCmd_GetHomeStatus(_bcId, axisId, ref sig);
            //CmdCheck(_ret);
            //axisStatus.原点 = (int)sig;
            //ZAux_Direct_GetDatumIn(_bcId, axisId, ref enable);
            //状态
            int state = 0;
            _ret = ZAux_Direct_GetAxisStatus(_bcId, axisId, ref state);
            CmdCheck(_ret);
            var bin2 = Convert.ToString(state, 2).PadLeft(24, '0').Reverse().ToArray();
            axisStatus.报警 = Convert.ToInt32(bin2[2].ToString());
            if (Convert.ToInt32(bin2[1].ToString()) == 1)
            {
                BCAlertHelper.Log($"【{axisName}】随动误差超限告警", LogLevel.严重);
                axisStatus.报警 = 1;
            }
            if (Convert.ToInt32(bin2[2].ToString()) == 1)
            {
                BCAlertHelper.Log($"【{axisName}】与远程轴通讯出错", LogLevel.严重);
                axisStatus.报警 = 1;
            }
            if (Convert.ToInt32(bin2[3].ToString()) == 1)
            {
                BCAlertHelper.Log($"【{axisName}】远程驱动器报错", LogLevel.严重);
                axisStatus.报警 = 1;
            }
            if (Convert.ToInt32(bin2[8].ToString()) == 1)
            {
                BCAlertHelper.Log($"【{axisName}】随动误差超限出错", LogLevel.严重);
                axisStatus.报警 = 1;
            }
            if (Convert.ToInt32(bin2[9].ToString()) == 1)
            {
                BCAlertHelper.Log($"【{axisName}】超过正向软限位", LogLevel.严重);
                axisStatus.报警 = 1;
            }
            if (Convert.ToInt32(bin2[10].ToString()) == 1)
            {
                BCAlertHelper.Log($"【{axisName}】超过负向软限位", LogLevel.严重);
                axisStatus.报警 = 1;
            }
            if (Convert.ToInt32(bin2[12].ToString()) == 1)
            {
                BCAlertHelper.Log($"【{axisName}】脉冲频率超过 MAX_SPEED 限 制.", LogLevel.严重);
                axisStatus.报警 = 1;
            }
            if (Convert.ToInt32(bin2[14].ToString()) == 1)
            {
                BCAlertHelper.Log($"【{axisName}】机械手指令坐标错误", LogLevel.严重);
                axisStatus.报警 = 1;
            }
            if (Convert.ToInt32(bin2[18].ToString()) == 1)
            {
                BCAlertHelper.Log($"【{axisName}】电源异常", LogLevel.严重);
                axisStatus.报警 = 1;
            }
            if (Convert.ToInt32(bin2[21].ToString()) == 1)
            {
                BCAlertHelper.Log($"【{axisName}】运动中触发特殊运动指令失败", LogLevel.严重);
                axisStatus.报警 = 1;
            }
            axisStatus.负限位 = GetIn(128 + axisId * 32); //Convert.ToInt32(bin2[5].ToString());
            axisStatus.正限位 = GetIn(129 + axisId * 32); //Convert.ToInt32(bin2[4].ToString());
            axisStatus.原点 = GetIn(130 + axisId * 32);
            axisStatus.正软限位 = Convert.ToInt32(bin2[9].ToString());
            axisStatus.负软限位 = Convert.ToInt32(bin2[10].ToString());
            axisStatus.位置 = Math.Round(GetAxisPosition(axisId), 2);

            if (axisStatus.报警 == 1)
            {
                //通知全局
                BcFactory.Instance.RunState(RunEnum.急停);
            }

            return axisStatus;
        }

        public int GetIn(int outPos)
        {
            uint val = 0;
            ZAux_Direct_GetIn(_bcId, outPos, ref val);
            return (int)val;
        }
        /// <summary>
        /// <see cref="GetAlm(ushort)"/>
        /// </summary>
        /// <param name="axisId"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public AlmModel GetAlm(ushort axisId)
        {
            return null;
        }
        /// <summary>
        /// <see cref="SetAlm(ushort, ushort, ushort)"/>
        /// </summary>
        /// <param name="axisId"></param>
        /// <param name="enable"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public int SetAlm(ushort axisId, ushort enable, ushort val)
        {
            return 0;
        }

        public void SetAxisPosition(ushort axis, double position)
        {
            throw new NotImplementedException();
        }

        public List<int> GetIn()
        {
            throw new NotImplementedException();
        }

        public List<int> GetOut()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 轴报警列表
        /// </summary>
        private Dictionary<int, string> DicAxisState = new Dictionary<int, string>()
        {
            { 2,"随动误差超限告警"},
            { 4,"与远程轴通讯出错"},
            { 8,"远程驱动器报错"},
            { 16,"正向硬限位"},
            { 32,"反向硬限位"},
            { 64,"找原点中"},
            { 128,"HOLD 速度保持信号输入"},
            { 256,"随动误差超限出错"},
            { 512,"超过正向软限位"},
            { 1024,"超过负向软限位"},
            { 2048,"CANCEL 执行中"},
            { 3402,"消息响应超时"},
            { 4096,"脉冲频率超过 MAX_SPEED"},
            { 16384,"机械手指令坐标错误"},
            { 20008,"网络打开失败"},
            { 20009,"句柄错误"},
            { 20010,"发送错误【运动控制掉线】"},
            { 262144,"电源异常"},
            { 2097152,"运动中触发特殊运动指令失败"},
            { 4194304,"告警信号输入"},
            { 8388608,"轴进入了暂停状态"},
        };

        #endregion

    }
}

